package gnssio

import (
	"dgo/goutils/utils"
	"io"
	"time"

	"fmt"

	"dgo/goutils/dnet"
	"errors"
	"strconv"
	"strings"
	"sync"

	serial "github.com/tarm/serial"
)

type CommSerial struct {
	id           string
	devHandle    io.ReadWriteCloser
	devid        string
	baudrate     int
	serialconf   *serial.Config
	lastrecv     time.Time
	rcvcnt       int
	lock         *sync.RWMutex
	checkTimer   *time.Timer
	isTerminated bool
	listeners    *utils.SafeList
}

func NewCommSerial(devid string, baudrate int, id string) *CommSerial {
	rval := &CommSerial{
		id:        id,
		devid:     devid,
		baudrate:  baudrate,
		lock:      new(sync.RWMutex),
		listeners: utils.NewSafeList(),
	}
	return rval
}

/*
*

	com:///dev/ttyS1[:115200]
*/
func NewCommSerialFromCmd(cmdstr string) *CommSerial {
	ok, cmdstr := utils.CheckTrimPrefix(cmdstr, "com://")
	if !ok {
		return nil
	}

	// name
	name := cmdstr
	strs := strings.SplitN(cmdstr, ":", 2)
	if len(strs) == 2 {
		name = strs[0]
	}
	cmdstr = strs[1]

	astr := cmdstr
	strs = strings.SplitN(cmdstr, "/", 2)
	if len(strs) == 2 {
		astr = strs[0]
	}

	baudrate, err := strconv.ParseInt(astr, 10, 32)
	if err != nil {
		baudrate = 115200
		utils.Log.Errf("str to int erro:%v", err)
	}

	return NewCommSerial(name, int(baudrate), name)
}

func CreateSerialCommFromCmdLineFunc(args ...interface{}) (rval interface{}, err error) {
	if len(args) < 1 {
		return nil, errors.New("至少要传入一个参数")
	}
	cmdStr := args[0].(string)
	if len(cmdStr) == 0 {
		return nil, errors.New("命令参数非法")
	}
	return NewCommSerialFromCmd(cmdStr), nil
}

func (this *CommSerial) checkSerialHandle() bool {
	if this.isTerminated {
		return false
	}

	if this.devHandle != nil {
		return true
	}

	s, err := serial.OpenPort(this.serialconf)
	if err != nil {
		str := fmt.Sprintf("open com(%s) fault:%v", this.devid, err)
		utils.Log.Warnf(str)
		return false
	} else {
		str := fmt.Sprintf("open com(%s) succ", this.devid)
		utils.Log.Debugf(str)
	}
	this.devHandle = s
	return true
}

func (this *CommSerial) innerStartRecv() {
	defer utils.PanicHandlerWithDebugString(fmt.Sprintf("com %s", this.devid))
	var buf []byte = nil
whilefor:
	for {
		if !this.checkSerialHandle() {
			if this.isTerminated {
				utils.Log.Info("CommSerial-ExitCycle-innerStartRecv-2")
				break whilefor
			}
			time.Sleep(2 * time.Second)
			continue
		}

		comr := this.devHandle
		if comr == nil {
			continue
		}

		if dnet.RECV_MAKE_NEW_BUFF || buf == nil {
			buf = make([]byte, dnet.RECV_BLOCK_SIZE)
		}
		//utils.Log.Info("innerStartRecv-1")
		n, err := comr.Read(buf)
		if this.isTerminated {
			utils.Log.Info("CommSerial-ExitCycle-innerStartRecv-1")
			break whilefor
		}
		if err != nil {
			msg := fmt.Sprintf("#read com(%s:%d) has err:%v\r\n", this.devid, this.baudrate, err)
			utils.Log.Warnf(msg)
			time.Sleep(2 * time.Second)
		} else if n > 0 {
			this.rcvcnt += n
			this.lastrecv = time.Now()
			this.listeners.ForEach(func(val interface{}) {
				val.(I_CommListener).OnReceive(this, buf, n)
			})
		} else {
			msg := fmt.Sprintf("#read com(%s:%d) fail n = 0\r\n", this.devid, this.baudrate)
			utils.Log.Warnf(msg)
		}
	}
	utils.Log.Info("CommSerial-ExitCycle-innerStartRecv-1")
}

// =================================================================
func (this *CommSerial) Start() {
	this.serialconf = &serial.Config{
		Name:        this.devid,
		Baud:        this.baudrate,
		ReadTimeout: time.Second * 3,
	}
	this.isTerminated = false
	this.lastrecv = time.Now()
	this.rcvcnt = 0
	utils.GoFunc2(this.innerStartRecv)

	this.checkTimer = time.AfterFunc(time.Second*20, this.innerCheckLastRecv)

}

func (this *CommSerial) innerCheckLastRecv() {
	if utils.SecondOf(this.lastrecv, time.Now()) > 20 {
		this.lastrecv = time.Now()
		if this.devHandle != nil {
			err := this.devHandle.Close()
			if err != nil {
				utils.Log.Warn("close com:%s err:%v", this.devid, err)
			}
			this.devHandle = nil
			msg := fmt.Sprintf("#read com:%s(%d) time out, will be reconnect\r\n", this.devid, this.baudrate)
			utils.Log.Warn(msg)
		} else {
			msg := fmt.Sprintf("#read com:%s(%d) time out, com is nil\r\n", this.devid, this.baudrate)
			utils.Log.Warn(msg)
		}

	}

	if !this.isTerminated {
		this.checkTimer = time.AfterFunc(time.Second*20, this.innerCheckLastRecv)
	}

}

func (this *CommSerial) Stop() {
	this.isTerminated = true
	utils.Log.Info("CommSerialStop")

	if this.checkTimer != nil {
		this.checkTimer.Stop()
		this.checkTimer = nil
	}

	if this.devHandle != nil {
		this.devHandle.Close()
		this.devHandle = nil
	}
}

func (this *CommSerial) Send(buf []byte, len int) int {
	if this.devHandle != nil {
		this.lock.Lock()
		defer this.lock.Unlock()
		n, _ := this.devHandle.Write(buf[:len])
		return n
	}

	return 0
}

func (this *CommSerial) AddListener(event I_CommListener) {
	if this.listeners.Exists(event) {
		utils.Log.Warn("I_CommListener已经存在请勿重复侦听")
		return
	}
	this.listeners.Add(event)
}

func (this *CommSerial) RemoveListener(event I_CommListener) {
	this.listeners.Remove(event)
}
